package org.test.aoplog.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;

/**
 * 测试切面日志
 *
 * @author wanglei
 * @date 2021/10/12
 */
@Component
@Aspect
public class AopLog {

    @Resource
    private HttpServletRequest request;

    Logger logger = LoggerFactory.getLogger(AopLog.class);

    private Date visitTime; //开始时间
    private Class<?> clazz; //访问的类
    private Method method; //访问的方法
    private String stackTracePackage = "org.test.aoplog"; // 这里过滤堆栈日志选择自己想看的包

    /**
     * 前置通知  主要是获取开始时间，执行的类是哪一个，执行的是哪一个方法
     *
     * @author wanglei
     * @date 2021/10/13
     * */
    @Before("execution(* org.test.aoplog.controller.*.*(..))")
    public void doBefore(JoinPoint jp) throws NoSuchMethodException {
        visitTime = new Timestamp(System.currentTimeMillis());//当前时间就是开始访问的时间
        clazz = jp.getTarget().getClass(); //具体要访问的类
        MethodSignature ms = (MethodSignature) jp.getSignature();
        method = ms.getMethod(); // 获取具体访问的方法
    }

    /**
     * 异常日志记录
     * 1）execution(* *(..))
     * //表示匹配所有方法
     * 2）execution(public * com. savage.service.UserService.*(..))
     * //表示匹配com.savage.server.UserService中所有的公有方法
     * 3）execution(* com.savage.server..*.*(..))
     * //表示匹配com.savage.server包及其子包下的所有方法
     *
     * @author wanglei
     * @date 2021/10/13
     * */
    @AfterThrowing(throwing="ex", pointcut="execution(* org.test.aoplog.controller.*.*(..))")
    public void doRecoveryActions(Throwable ex)
    {
        // 请求日志
        String requestLog = getRequestLog();
        logger.info(requestLog);
        // 错误日志
        String stackTrace = getStackTrace(ex);
        logger.error(ex.toString());
        logger.error(stackTrace);
    }

    /**
     * 错误拦截最好分两块，一块拦截请求报错，一块拦截系统报错
     * 这个全局错误拦截貌似有点问题，会导致项目无法启动
     *
     * @author wanglei
     * @date 2021/10/27
     * */
//    @AfterThrowing(throwing="ex", pointcut="execution(* *(..))")
//    public void systemThrowing(Throwable ex)
//    {
//        // 错误日志
//        String stackTrace = getStackTrace(ex);
//        logger.error(ex.toString());
//        logger.error(stackTrace);
//    }

    /**
     * 整理请求日志
     *
     * @author wanglei
     * @date 2021/10/13
     * */
    public String getRequestLog(){
        if(visitTime != null){
            long time = new Date().getTime() - visitTime.getTime(); //获取访问的时长
            String url;
            // 获取url
            if (clazz != null && method != null && clazz != AopLog.class) {
                RequestMapping classAnnotation = clazz.getAnnotation(RequestMapping.class);
                if (classAnnotation != null) {
                    String[] classValue = classAnnotation.value();
                    //2.获取方法上的@RequestMapping(xxx)
                    RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                    if (methodAnnotation != null) {
                        String[] methodValue = methodAnnotation.value();
                        url = classValue[0] + methodValue[0];
                        //获取访问的ip
                        String ip = request.getRemoteAddr();
                        // 请求参数
                        Map<String, String[]> parameterMap = request.getParameterMap();
                        String username = "admin";
                        return String.format("time=%s毫秒, ip=%s, class=%s, method=%s, params=%s, url=%s, username=%s, visitTime=%s",
                                time, ip, clazz.getName(), method.getName(), map2str(parameterMap), url, username, visitTime);
                    }
                }
            }
        }
        return "";
    }

    /**
     * 格式化堆栈日志
     *
     * @author wanglei
     * @date 2021/10/13
     * */
    public String getStackTrace(Throwable ex){
        StringBuilder sb = new StringBuilder();
        StackTraceElement[] stackTrace = ex.getStackTrace();
        for(StackTraceElement ste : stackTrace){
            String className = ste.getClassName();
            String methodName = ste.getMethodName();
            String fileName = ste.getFileName();
            int lineNumber = ste.getLineNumber();
            // 这里过滤堆栈日志选择自己想看的包
            if(className.contains(stackTracePackage)){
                sb.append(className).append(".").append(methodName).append("(").append(fileName).append(":").append(lineNumber).append(")").append("\r\n");
            }
        }
        if(sb.toString().equals("")){
            return "";
        }
        return sb.substring(0, sb.length()-1);
    }

    /**
     * map转str
     *
     * @author wanglei
     * @date 2021/10/13
     * */
    private String map2str(Map<String, String[]> map) {
        StringBuilder sb = new StringBuilder();
        if (map.size() > 0) {
            for (Map.Entry<String, String[]> entry : map.entrySet()) {
                String[] values = entry.getValue();
                StringBuilder vb = new StringBuilder();
                String value = "";
                if(values != null && values.length>0){
                    for(String s : values){
                        vb.append(s).append(",");
                    }
                    value = vb.substring(0, vb.length()-1);
                }
                sb.append(entry.getKey()).append("=").append(value).append("&");
            }
            return sb.substring(0, sb.length()-1);
        }
        else{
            return "";
        }
    }
}
